﻿using System;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;

namespace Chat
{
    public partial class Chat : Form
    {
        //voor het geval dat de naam leeg is
        private string username = "Unknown";

        //verstuurt berichten naar server
        private StreamWriter Send;

        //ontvangt berichten van server
        private StreamReader Recieve;

        //de connectie naar de server
        private TcpClient tcpServer;

        //vangt berichten van andere threads af
        private delegate void updateLogCallback(string message);

        //vangt users af
        private delegate void updateUserlistCallback(string user);

        //zet users in userlistbox
        private delegate void setUserlistCallback();

        //vangt disconnects af
        private delegate void closeConnectionCallback(string reason);

        //thread waar je berichten mee binnenkrijgt
        private Thread threadMessaging;
        
        //ip port en connectiestatus (standaard false)
        private IPAddress ip;
        private int port;
        private Boolean connected;

        //array met usernames en de counter die de index van de array aangeeft
        public static string[] userlist = new string[30];
        public static int usercounter = 0;

        public Chat()
        {
            //als de applicatie afgesloten word zonder op disconnect te drukken moet er gedisconnect worden
            Application.ApplicationExit += new EventHandler(OnApplicationExit);
            InitializeComponent();
        }

        public void OnApplicationExit(object sender, EventArgs e)
        {
            if (connected == true)
            {
                //close alles
                connected = false;
                Send.Close();
                Recieve.Close();
                tcpServer.Close();
            }
        }

        private void btnChatserver_Click(object sender, EventArgs e)
        {
            ChatServer.ChatServer chatserver = new ChatServer.ChatServer();
            chatserver.Show();
        }

        private void buttonConnect_Click(object sender, EventArgs e)
        {
            if (connected == false)
            {
                //als er geen variabelen ingevoerd zijn moet er geen connectie gemaakt worden
                if (txtIp.Text == "" || txtPort.Text == "")
                {
                    CloseConnection("Voer een IP adres en/of een port in\r\n");
                }
                else
                {
                    PrepConnection();
                }
            }
            //als er al een connectie is moet je kunnen disconnecten
            else
            {
                CloseConnection("Disconnected by user \r\n");
                txtUserlist.Clear();
            }
        }

        private void PrepConnection()
        {
            //username, port en ip adres ophalen uit form
            ip = IPAddress.Parse(txtIp.Text);
            port = Convert.ToInt32(txtPort.Text);
            username = txtUsername.Text;

            //connectie maken met verkregen variabelen
            tcpServer = new TcpClient();
            try
            {
                tcpServer.Connect(txtIp.Text, port);
                //indien we connected zijn -> connected = true
                connected = true;
            }
            catch
            {
                //indien we niet connected zijn
                CloseConnection("Er is geen server op dit adres gevonden\r\n");
            }

            if (connected == true)
            {
                InitConnection();
            } 
        }

        private void InitConnection()
        {
            //fields disabelen, message versturen enablen
            txtIp.Enabled = false;
            txtPort.Enabled = false;
            txtUsername.Enabled = false;
            buttonSend.Enabled = true;
            txtMessage.Enabled = true;
            buttonConnect.Text = "Disconnect";

            //username naar server sturen
            Send = new StreamWriter(tcpServer.GetStream());
            Send.WriteLine(username);
            Send.Flush();

            //thread starten waar je berichten mee binnen krijgt
            threadMessaging = new Thread(new ThreadStart(RecieveMessages));
            threadMessaging.Start();
        }

        private void RecieveMessages()
        {
            //berichten van de server verkrijgen
            Recieve = new StreamReader(tcpServer.GetStream());

            //controleren of we connected zijn, als we een 1 doorkrijgen zijn we connected
            string connectionResponse = Recieve.ReadLine();

            if (connectionResponse[0] == '1')
            {
                //doorgeven dat we connected zijn
                this.Invoke(new updateLogCallback(this.UpdateLog), new object[] { "Connected!" });
            }
            else //als we niet connected zijn
            {
                string Reason = "Connection failed!\r\n";
                 
                //reden uit response message van de server halen, deze bevind zich op nummer 3
                Reason += connectionResponse.Substring(2, connectionResponse.Length - 2);
                
                //form updaten met de reden
                this.Invoke(new closeConnectionCallback(this.CloseConnection), new object[] {Reason});

                //exit
                return;
            }

            while (connected == true)
            {
                string recievedmessage;
                
                try
                {
                    recievedmessage = Recieve.ReadLine();

                    if (recievedmessage[0] == '3')
                    {
                        this.Invoke(new updateUserlistCallback(this.getUserlist), new object[] { recievedmessage.Substring(2, recievedmessage.Length - 2) });
                        recievedmessage = null;
                    }
                    else if (recievedmessage[0] == '4')
                    {
                        this.Invoke(new setUserlistCallback(this.updateUserlist));
                        usercounter = 0;
                        recievedmessage = null;
                    }
                    else
                    {
                        this.Invoke(new updateLogCallback(this.UpdateLog), new object[] { recievedmessage });
                        recievedmessage = null;
                    }
                }
                catch(Exception ex)
                {
                    if (ex is SocketException || ex is IOException)
                    {
                        Send.Close();
                        Recieve.Close();
                        tcpServer.Close();
                        threadMessaging.Abort();
                        connected = false;
                        recievedmessage = null;
                        Array.Clear(userlist, 0, 30);
                    }
                }
            }
        }

        //wordt aangeroepen door een andere thread. update de textbox
        private void UpdateLog(string Message)
        {
            txtChat.AppendText(Message+"\r\n");
        }

        //verzamelt de users
        private void getUserlist(string user)
        {
            userlist[usercounter] = user;
            usercounter++;
        }
        
        //update de lijst met users in de correcte box
        private void updateUserlist()
        {
            txtUserlist.Clear();

            for (int i = 0; i < userlist.Length; i++)
            {
                if (userlist[i] == null)
                {
                    break;
                }

                txtUserlist.AppendText(userlist[i] + "\r\n");
            }
            
            Array.Clear(userlist, 0, 30);
        }

        //verzendknop (bericht)
        private void buttonSend_Click(object sender, EventArgs e)
        {
            SendMessage();
        }

        //enter toets = send
        private void txtMessage_KeyPress(object Sender, KeyPressEventArgs e)
        {
            if (e.KeyChar == (char)13)
            {
                SendMessage();
            }
        }

        //verzend het bericht
        private void SendMessage()
        {
            if (txtMessage.Lines.Length >= 1)
            {
                Send.WriteLine(txtMessage.Text);
                Send.Flush();
                txtMessage.Lines = null;
            }

            //nadat het bericht verstuurd is moet het veld leeg zijn
            txtMessage.Text = "";
        }

        //sluit de connectie
        private void CloseConnection(string Reason)
        {
            //reden van disconnect sturen
            txtChat.AppendText(Reason+"\r\n");

            //velden enablen / disablen
            txtIp.Enabled = true;
            txtPort.Enabled = true;
            txtUsername.Enabled = true;
            buttonSend.Enabled = false;
            txtMessage.Enabled = false;
            buttonConnect.Text = "Connect";
            connected = false;

            //objecten sluiten (als ze al aangeroepen zijn)
            if (Send != null)
            {
                Send.Close();
            }
            if (Recieve != null)
            {
                Recieve.Close();
            }
            if (tcpServer != null)
            {
                tcpServer.Close();
            }
        }
    }
}